package xquery;
import java.util.*;

import org.w3c.dom.*;
public class XQueryJoinExp extends XQuery{
	XQExpression [] forExp;
	ArrayList<ArrayList<String>> varLists;
	@Override
	public XPathData eval(XQContext context, ArrayList<Node> currentNodeList)
	{
		ArrayList<Node> resultList1 = forExp[0].eval(context, currentNodeList).getList();
		ArrayList<Node> resultList2 = forExp[1].eval(context, currentNodeList).getList();
		XPathData result = new XPathData();
		ArrayList<Node> resultList = new ArrayList<Node>();
		HashMap<IndexNode,ArrayList<Node>> joinTable  = new HashMap<IndexNode,ArrayList<Node>>();
		IndexNode indexNode = null;
		ArrayList<Node> list = null;
		ArrayList<Node> index = null;
		Node tuple;
		for(int i=0;i<resultList2.size();i++)
		{
			tuple = resultList2.get(i);
			NodeList children = tuple.getChildNodes();
			indexNode = new IndexNode();
			index = new ArrayList<Node>();
			for(int j=0;j<children.getLength();j++)
			{			
				Node child = children.item(j);
				if(varLists.get(1).contains(child.getNodeName()))
				{
					index.add(child.getFirstChild());
				}
			}
			indexNode.indices = index;
			if(joinTable.containsKey(indexNode))
			{
				joinTable.get(indexNode).add(tuple);
			}
			else
			{
				list = new ArrayList<Node>();
				list.add(tuple);
				joinTable.put(indexNode, list);
			}
		}	
		for(int i = 0;i<resultList1.size();i++)
		{
			indexNode = new IndexNode();
			index = new ArrayList<Node>();
			tuple = resultList1.get(i);
			NodeList children = tuple.getChildNodes();
			for(int j=0;j<children.getLength();j++)
			{			
				Node child = children.item(j);
				if(varLists.get(0).contains(child.getNodeName()))
				{
					index.add(child.getFirstChild());
				}
			}
			indexNode.indices = index;
			if(joinTable.containsKey(indexNode))
			{
				list = joinTable.get(indexNode);
				for(int j=0;j<list.size();j++)
				{
					/*
					Node child = tuple.getFirstChild();
					Node newNode = list.get(j).cloneNode(true);
					while(child != null)
					{						 
						newNode.appendChild(child.cloneNode(true));
						child = child.getNextSibling();
					}
					resultList.add(newNode);
					*/

					XQueryNode newNode = new XQueryNode();
					newNode.childList = new ArrayList<Node>();
					Node child = tuple.getFirstChild();
					while(child != null)
					{						 
						newNode.childList.add(child);
						child = child.getNextSibling();
					}
					child = list.get(j).getFirstChild();
					while(child != null)
					{						 
						newNode.childList.add(child);
						child = child.getNextSibling();
					}
					resultList.add(newNode);					
				}	
			}
		}
		result.set(resultList);
		return result;
	}
	public String toString()
	{
		StringBuffer sb = new StringBuffer("(");
		for(int i=0;i<forExp.length;i++)
		{
			sb.append(forExp[i]);
		}
		for(int i=0;i<varLists.get(0).size();i++)
		{
			sb.append("[ ");
			sb.append(varLists.get(0).get(i));
			sb.append(" , ");
			sb.append(varLists.get(1).get(i));
			sb.append(" ] ");
		}
		sb.append(")");
		return sb.toString();
	}
}
class IndexNode
{
	ArrayList<Node> indices;
	public boolean equals(Object obj)
	{
        if(obj == this)
        {              
        	return true;
        }
        if(obj == null || obj.getClass() != this.getClass())
        {
        	return false;
        }         
		IndexNode indexNode = (IndexNode)obj;
		if (indices.size() != indexNode.indices.size())
			return false;
		for(int i=0;i<indices.size();i++)
		{
			if (!indices.get(i).isEqualNode(indexNode.indices.get(i)))
			{
				return false;
			}
		}
		return true;
	}
	public int hashCode()
	{
		int code = 0;
/*		for(int i=0;i<indices.size();i++)
		{
			if(indices.get(i).getNodeType() == Node.ELEMENT_NODE)
			{
				code += indices.get(i).getNodeName().hashCode();
			}
			else if(indices.get(i).getNodeType() == Node.TEXT_NODE)
			{
				code += indices.get(i).getNodeValue().hashCode();
			}
		}
		return code;*/
		Queue<Node> queue = new LinkedList<Node>();
		NodeList children;
		Node current = null;
		String descendants = "";
		for(Node node: indices)
		{
			queue.add(node);
			while(!queue.isEmpty())
			{
				current = queue.remove();
				if(current!= null)
				{
					if(current.getNodeType() == Node.ELEMENT_NODE)
					{
						descendants += current.getNodeName();
					}
					else if(current.getNodeType() == Node.TEXT_NODE)
					{
						descendants += current.getNodeValue();
					}
					children = current.getChildNodes();
					for(int i=0;i<children.getLength();i++)
					{
						queue.add(children.item(i));
					}
				}
			}
		}
		code = descendants.hashCode();
		return code;
	}
}
